package org.one.stone.soup.grfxML;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.lang.reflect.Constructor;

import org.one.stone.soup.math.IntMath;
import org.one.stone.soup.mjdb.data.field.FloatField;
import org.one.stone.soup.mjdb.data.field.IntField;
import org.one.stone.soup.mjdb.data.field.LockException;
import org.one.stone.soup.mjdb.data.field.Point2DField;
import org.one.stone.soup.mjdb.data.field.Point3DField;
import org.one.stone.soup.xml.XmlElement;
  /*
 * Copyright 2000-2001 Wet-Wired.com Ltd.,
 * Portsmouth England
 * All rights reserved.
 */

/**
	*
	* @author Nik Cross
*/
public class GrfxMLProcessor {

	static int translationCount = 0;
	static int behaviourCount = 0;
	static int actionCount = 0;
	static int nodeCount = 0;
	static int z = -1;
public static void add(IntField valueA,IntField valueB, IntField target) throws LockException{

	target.setValue(valueA.getValue()+valueB.getValue() , target);
}
public static void add(Point2DField vectorA,Point2DField vectorB, Point2DField target) throws LockException {

	target.setValueX(vectorA.getValueX()+vectorB.getValueX(),target);
	target.setValueY(vectorA.getValueY()+vectorB.getValueY(),target);
}
public static void angle(Point2DField point,IntField angle) throws LockException {

	int nix;
	int niy;

	nix = point.getValueX();
	niy = point.getValueY();

	int a = IntMath.angle(nix,niy);

	angle.setValue( a/*%360*/,point );
	/*if(a>0)
	{
		angle.setValue((a+angle.getValue()-90)%360,point);
	}*/

//	System.out.println("Angle = "+a+" "+APIInterface.getController().frameCounter+" x="+nix+" y="+niy);

//	System.out.println("frameNo:"+grfxMLEngine.frameCounter+"ref:"+point.getRef()+" x="+nix+" y="+niy+" a:"+IntMath.angle(nix,niy));
}
public static void copy(Point2DField vector, Point2DField target) throws LockException{

	target.setValueX(vector.getValueX(),target);
	target.setValueY(vector.getValueY(),target);
}
public static void copy(Point3DField vector, Point3DField target) throws LockException{

	target.setValueX(vector.getValueX(),target);
	target.setValueY(vector.getValueY(),target);
	target.setValueZ(vector.getValueZ(),target);
}
public static int distance(Point2DField origin, Point2DField position) throws LockException{

	DataPoint result = new DataPoint();

	sub(position,origin,result);

	return IntMath.distance( result.getValueX(),result.getValueY() );
}
public static Color getColorFor(DataColor colorF) {

	Color color = new Color( colorF.getValueX(),colorF.getValueY(),colorF.getValueZ(),colorF.alpha );
	return color;
}
public static void middle(Point2DField vectorA,Point2DField vectorB, Point2DField target) throws LockException{

	target.setValueX( ((vectorA.getValueX()-vectorB.getValueX())/2) + vectorB.getValueX() , target);
	target.setValueY( ((vectorA.getValueY()-vectorB.getValueY())/2) + vectorB.getValueY() , target);
}
public static void mouseMoved(GrfxMLEngine engine,DataMouse mouse,MouseEvent e) {

	try{
		GrfxMLProcessor.copy(
			mouse.positionView,
			mouse.lastPositionView
		);

		mouse.positionView.setValueX(e.getX()+engine.mouseOffset.x,mouse);
		mouse.positionView.setValueY(e.getY()+engine.mouseOffset.y,mouse);

	GrfxMLProcessor.sub(
		mouse.positionView,
		mouse.lastPositionView,
		mouse.velocityView
	);

	} catch (LockException le) {
	} //dummy catch

	if ( mouse==engine.BASE.MOUSE && mouse.getButtonDown().getValue()==true && mouse.sticky.getValue() == true) {
		try {

			GrfxMLProcessor.sub(
					mouse.positionView,
					mouse.lastPositionView,
					mouse.velocityView
			);

			GrfxMLProcessor.add(engine.BASE.ROOT.translation.position, mouse.velocityView, engine.BASE.ROOT.translation.position);
		} catch (LockException le) {
		} //dummy catch

	}
}
public static void mousePressed(DataMouse mouse,MouseEvent e) {

	try{
		if(mouse.buttonReleased.getValue()==false)
		{
			mouse.getButtonDown().setValue(true,mouse);
			mouse.buttonPressed.setValue(true,mouse);
			mouse.buttonDoubleClicked.setValue(true,mouse);

			if((e.getModifiers() & MouseEvent.BUTTON3_MASK) >0)
			{
				mouse.getButton2Down().setValue(true,mouse);
				mouse.button2Pressed.setValue(true,mouse);
				if(e.getClickCount()>1)
				{
					mouse.button2DoubleClicked.setValue(true,mouse);
				}
			}
			else
			{
				mouse.getButton1Down().setValue(true,mouse);
				mouse.button1Pressed.setValue(true,mouse);
				if(e.getClickCount()>1)
				{
					mouse.button1DoubleClicked.setValue(true,mouse);
				}
			}
		}
	}catch(LockException le){}//dummy catch

	//mouse.requestFocus();
}
public static void mouseReleased(DataMouse mouse,MouseEvent e) {

	try{
		mouse.getButtonDown().setValue(false,mouse);
		mouse.buttonReleased.setValue(true,mouse);

		if((e.getModifiers() & MouseEvent.BUTTON3_MASK) >0)
		{
			mouse.getButton2Down().setValue(false,mouse);
			mouse.button2Released.setValue(true,mouse);
		}
		else
		{
			mouse.getButton1Down().setValue(false,mouse);
			mouse.button1Released.setValue(true,mouse);
		}

	}catch(LockException le){}//dummy catch

}
public static void mult(IntField valueA,FloatField valueB, IntField target) throws LockException{

	target.setValue((int)(valueA.getValue()*valueB.getValue()) , target);
}
public static void mult(IntField valueA,IntField valueB, IntField target) throws LockException{

	target.setValue(valueA.getValue()*valueB.getValue() , target);
}
public static int process(GrfxMLEngine engine,int currentZ, DataBehaviour behaviour, DataNode parent, DataTranslation pt) {
	try {
		if (parent.translation.visibleView.getValue() == false)
			return currentZ;
		int zOrder = parent.getZOrder();
		if (zOrder < currentZ) {
			return currentZ;
		}

		//APIInterface.getMouse().setOver(parent);

		boolean isOver = false;

		// no mouse events
		if (engine.BASE.MOUSE.getCutEventsSize()==0)
			return currentZ;

		// process mouseEvents
		DataMouse mouse = engine.BASE.MOUSE.getClone();

		/*		boolean inBug = false;
		if(
		events.size()==2 &&
		((MouseEvent)events.elementAt(0)).getID()==MouseEvent.MOUSE_PRESSED &&
		((MouseEvent)events.elementAt(1)).getID()==MouseEvent.MOUSE_RELEASED
		)
		{
		inBug=true;
		//			System.out.println("One frame press release");
		}*/

		behaviourCount++;
		//mouse.resetButtons();
		for (int loopE = 0; loopE < engine.BASE.MOUSE.getCutEventsSize(); loopE++) {
			//mouse.resetButtons();
			processMouseEvent(engine,engine.BASE.MOUSE.getCutEvent(loopE), mouse);

			//===========================================================================
			//Translate the mouse for this node
			mouse.translate(parent.translation);

			//===========================================================================
			//Calculate over
			DataShape shape = parent.shape;
			boolean over = false;
			for (int loop = 0; loop < shape.size(); loop++) {
				Geometry geo = shape.get(loop).getGeometry(engine);
				if (geo != null) {
					if (geo.contains(mouse.position, mouse.positionView)) {

						if(
							//pt.isWindowDefined()==false ||
							parent.getTranslation().inWindow( mouse.positionView )
						)
						{
							over = true;
							break;
						}
					}
				}
			}

			/*		if(inBug && over)
			{
			System.out.println("In Bug");
			}*/

			//If over
			if (over && zOrder >= currentZ) {
				currentZ = zOrder;

//				APIInterface.getMouse().setOver(parent);

				DataNode old = (DataNode) mouse.getOver();
				if (old != null && old.getZOrder() < zOrder) {
					old.getBehaviour().getOver().setValue(false, parent);
				}

				//Set over
				behaviour.over.setValue(true, parent);
				mouse.setOver(parent);
				mouse.translate(pt);
			} else {
				behaviour.over.setValue(false, parent);
				/*if (mouse.button2Pressed.getValue() == true)
				{
				behaviour.selected.setValue(false,parent);
				} Removed right button selected release*/
			}

			//===========================================================================
			//Process button

			//If selectable
			if (behaviour.selectable.getValue() == true) {

				//===========================================================================
				//Process button up
				/*			if (behaviour.toggled.getValue() == false && behaviour.draggable.getValue()==false) {
				behaviour.selected.setValue(false, behaviour);
				mouse.setHeld(null);
				//System.out.println("B");
				}
				if(mouse.buttonReleased.getValue()==true && behaviour.draggable.getValue()==true && behaviour.toggled.getValue()==false)
				{
				behaviour.selected.setValue(false, behaviour);
				mouse.setHeld(null);
				//System.out.println("B");
				}*/
				if (mouse.buttonReleased.getValue() == true && behaviour.toggled.getValue() == false && !DataKey.ctrlDown())
				{
					behaviour.selected.setValue(false, behaviour);
					if(engine.BASE.MOUSE.getHeld()==parent)
					{
						behaviour.dropped.setValue(true, behaviour);
						engine.BASE.MOUSE.setHeld(null);
					}
					//System.out.println("B");
				}

				//===========================================================================
				//Process button down

				//If mouse over geometry
				if (over && zOrder >= currentZ) {

					//Set cursor to hand
					engine.BASE.MOUSE._cursorMode = Cursor.HAND_CURSOR;

					//If button down
					if (engine.BASE.MOUSE.buttonPressed.getValue() == true) {
						// Reset mouse down so only activates one node
						/*mouse.buttonPressed.setValue(false,behaviour); Removed mouse button reset*/

						if(!DataKey.shiftDown())
						{
							mouse.buttonPressed.setValue(false,behaviour);
							engine.BASE.MOUSE.buttonPressed.setValue(false,behaviour);
							/*if(behaviour.selected.getValue()==false)
							{
								engine.BASE.MOUSE.resetButtons();
							}*/
						}

						//---------------------------------------------------------------------------
						//If toggled
						if (behaviour.toggled.getValue() == true) {
							//If already selected
							if (behaviour.selected.getValue() == true) {
								//Set unselected
								behaviour.selected.setValue(false, behaviour);
								engine.BASE.MOUSE.setHeld(null);
								setFocus(engine,parent.shape, false);
							}
							//If not already selected
							else {
								//Set selected
								behaviour.selected.setValue(true, behaviour);
								engine.BASE.MOUSE.setHeld(parent);
								setFocus(engine,parent.shape, true);
							}
						}
						//---------------------------------------------------------------------------
						//If not toggled
						else {
							//Set selected
							behaviour.selected.setValue(true, behaviour);
							engine.BASE.MOUSE.setHeld(parent);
						}
						//---------------------------------------------------------------------------
					}
				} // end of if over
			}


			//===========================================================================
			//Process velocity

			//If selected
			if (behaviour.selected.getValue() == true && !DataKey.ctrlDown() && mouse.getButtonDown().getValue()==true && mouse.getSticky().getValue()==false) {

				if (behaviour.rotatable.getValue() == true) {
					mouse.translate(pt);

					angle(mouse.position, parent.translation.rotation);
					behaviour.rotating.setValue(true, parent);
				}
				else
				{
					behaviour.rotating.setValue(false, parent);
				}

				//Transfer mouse velocity to behaviour velocity
				if (behaviour.draggable.getValue() == true) {
					mouse.translate(pt);
					copy(mouse.velocity, behaviour.velocity);
					//Set cursor to move
					engine.BASE.MOUSE._cursorMode = Cursor.MOVE_CURSOR;
					//Add behaviour velocity to parent node
					add(parent.translation.position, behaviour.velocity, parent.translation.position);
					behaviour.dragging.setValue(true, parent);
				}
				else
				{
					behaviour.dragging.setValue(false, parent);
				}
			}
			else
			{
				behaviour.rotating.setValue(false, parent);
				behaviour.dragging.setValue(false, parent);
			}

			//===========================================================================

			if (over)
				isOver = true;
		}
		if (isOver && zOrder > currentZ) {
			return zOrder;
		}
	} catch (LockException le) {
	} //dummy catch

	return currentZ;
}
public static void process(DataAction action) throws LockException {
	if (action.running.getValue() == true) {

		actionCount++;

		action.start.setValue(false, action);
		action.end.setValue(false, action);
		if (action.stopTrigger.getValue() == true) {
			action.stopTrigger.setValue(false, action);
			action.running.setValue(false, action);
			return;
		}
		if (action.direction.getValue().equals(ProcessConstant.FORWARD)) {
			action.currentStep.inc(action);
			if (action.currentStep.getValue() > action.steps.getValue()) {
				if (action.looped.getValue() == true) {
					action.currentStep.setValue(0, action);
					action.start.setValue(true, action);
					action.end.setValue(true, action);
				} else
					if (action.pingpong.getValue() == true) {
						action.currentStep.setValue(action.steps.getValue(), action);
						action.direction.setValue(ProcessConstant.BACKWARD, action);
						action.end.setValue(true, action);
					} else {
						action.running.setValue(false, action);
						action.end.setValue(true, action);
					}
			}
		} else
			if (action.direction.getValue().equals(ProcessConstant.BACKWARD)) {
				action.currentStep.dec(action);
				if (action.currentStep.getValue() <= 0) {
					if (action.looped.getValue() == true) {
						action.currentStep.setValue(action.steps.getValue(), action);
						action.start.setValue(true, action);
						action.end.setValue(true, action);
					} else
						if (action.pingpong.getValue() == true) {
							action.currentStep.setValue(0, action);
							action.direction.setValue(ProcessConstant.FORWARD, action);
							action.start.setValue(true, action);
						} else {
							action.running.setValue(false, action);
							action.end.setValue(true, action);
						}
				}
			}
	} else {
		action.start.setValue(false, action);
		action.end.setValue(false, action);
		if (action.startTrigger.getValue() == true) {
			action.startTrigger.setValue(false, action);
			action.start.setValue(true, action);
			action.running.setValue(true, action);
			action.currentStep.setValue(0, action);
		}
	}
	if (action.running.getValue() == true) {
		action.process();
	}
}
public static void process(APInterface api,DataAnchor anchor) throws LockException {
	if (anchor.trigger.getValue() == true) {
		anchor.trigger.setValue(false,anchor);

		if (anchor instanceof DataAnchorToFile) {
			DataAnchorToFile af = (DataAnchorToFile) anchor;
			api.load(af.file);
//			APIInterface.sendCommand("Load:"+af.file.getValue());
		}
		else if (anchor instanceof DataAnchorToWebPage) {
			DataAnchorToWebPage aW = (DataAnchorToWebPage) anchor;
			api.showWebPage(aW.file.getValue());
		}
		else if (anchor instanceof DataAnchorSendCommand) {
			DataAnchorSendCommand aC = (DataAnchorSendCommand) anchor;
			api.sendCommand(aC.command.getValue());
		}
		else if (anchor instanceof DataAnchorBack) {
			api.back();
		}
		else if (anchor instanceof DataAnchorReload) {
			api.reload();
		}
	}
}
public static void process(APInterface api,DataAnchors anchors) throws LockException {
	for (int loop = 0; loop < anchors.size(); loop++) {
		process(api,anchors.get(loop));
	}
}
public static void process(GrfxMLEngine engine,APInterface api,DataNode node, DataTranslation pt) {

 try {

	process(node.process);

	if (node.behaviour != null) {
		z = process(engine,z,node.behaviour, node, pt);
	}
	else if(node.translation.visibleView.getValue()==true)
	{
		for (int loop = 0; loop < node.shape.size(); loop++) {
			Geometry geo = node.shape.get(loop).getGeometry(engine);
			if (geo != null) {
				if (geo.contains(engine.BASE.MOUSE.position, engine.BASE.MOUSE.positionView))
				{
					if(z<node.getZOrder())
					{
						z = node.getZOrder();
						DataNode old = (DataNode)engine.BASE.MOUSE.getOver();
						if(old!=null && old.getZOrder()<z)
						{
								old.getBehaviour().getOver().setValue(false,node);
						}

					}
					break;
				}
			}
		}
	}

	process(api,node.anchors);

	nodeCount++;

 } catch (Exception e) {
} //dummy catch

}
public static void process(DataProcess process) throws LockException {
	for (int loop = 0; loop < process.size(); loop++) {
		process(process.get(loop));
	}
}

public static void processMouseEvent(GrfxMLEngine engine,MouseEvent e, DataMouse mouse)
{
	switch( e.getID() )
	{
		case MouseEvent.MOUSE_MOVED:
			mouseMoved(engine,mouse,e);
			break;
		case MouseEvent.MOUSE_PRESSED:
			mousePressed(mouse,e);
			break;
		case MouseEvent.MOUSE_RELEASED:
			mouseReleased(mouse,e);
			break;
	}
}
static void resetCounters()
{
	behaviourCount=0;
	actionCount=0;
	translationCount=0;
	nodeCount=0;
	z=-1;
}
public static void reverseRotate(Point2DField point,IntField angle,Point2DField target) throws LockException {

	int nix;
	int niy;

	nix = IntMath.cos(-angle.getValue(),point.getValueX())-IntMath.sin(-angle.getValue(),point.getValueY());
	niy = IntMath.sin(-angle.getValue(),point.getValueX())+IntMath.cos(-angle.getValue(),point.getValueY());

	target.setValueX(nix,point);
	target.setValueY(niy,point);

}
public static void reverseScale(Point2DField vector, FloatField scale,Point2DField target) throws LockException{

	target.setValueX( (int)(vector.getValueX()/scale.getValue()),target );
	target.setValueY( (int)(vector.getValueY()/scale.getValue()),target );
}
public static void reverseTranslate(DataPoint source,TagModifier modifier, DataTranslation action, DataPoint target) throws LockException
{
	switch(modifier.flavour)
	{
		case TagModifier.POINT_TYPE_POINT_CODE:
			copy(source,target);

			sub(target,action.positionView,target);
			reverseScale(target,action.scaleView,target);
			reverseRotate(target,action.rotationView,target);
			break;
		case TagModifier.POINT_TYPE_VECTOR_CODE:
			copy(source,target);

			reverseScale(target,action.scaleView,target);
			reverseRotate(target,action.rotationView,target);
			break;
	}
}
public static void rotate(Point2DField point,IntField angle,Point2DField target) throws LockException{

	if(angle.getValue()%360==0)
	{
		copy(point,target);
		return;
	}

	int nix;
	int niy;

	nix = IntMath.cos(angle.getValue(),point.getValueX())-IntMath.sin(angle.getValue(),point.getValueY());
	niy = IntMath.sin(angle.getValue(),point.getValueX())+IntMath.cos(angle.getValue(),point.getValueY());

	target.setValueX(nix,point);
	target.setValueY(niy,point);

}
public static void rotate(Point2DField origin,Point2DField point,IntField angle,Point2DField target) throws LockException {

	int nix;
	int niy;

	nix = IntMath.cos(angle.getValue(),point.getValueX()-origin.getValueX())-IntMath.sin(angle.getValue(),point.getValueY()-origin.getValueY());
	niy = IntMath.sin(angle.getValue(),point.getValueX()-origin.getValueX())+IntMath.cos(angle.getValue(),point.getValueY()-origin.getValueY());

	target.setValueX(nix+origin.getValueX(),point);
	target.setValueY(niy+origin.getValueY(),point);

}
public static void scale(FloatField sourceScale, FloatField scale,FloatField targetScale) throws LockException{

	if(scale.getValue()==1)
	{
		targetScale.setValue( (float)sourceScale.getValue() , targetScale);
		return;
	}

	targetScale.setValue( (float)(sourceScale.getValue()*scale.getValue()) ,targetScale);

}
public static void scale(Point2DField vector, FloatField scale,Point2DField target) throws LockException{

	if(scale.getValue()==1)
	{
		target.setValueX( vector.getValueX() ,target);
		target.setValueY( vector.getValueY() ,target);
		return;
	}

	target.setValueX( (int)(vector.getValueX()*scale.getValue()) ,target);
	target.setValueY( (int)(vector.getValueY()*scale.getValue()) ,target);
}
private static void setFocus(GrfxMLEngine engine,DataShape shape, boolean state) throws LockException{
	if(engine.BASE.KEY._focus!=null)
	{
		engine.BASE.KEY._focus.focusField.setValue(false,engine.BASE.KEY);
	}

	for (int loop = 0; loop < shape.size(); loop++) {
		Geometry geo = shape.get(loop).getGeometry(engine);
		if (geo != null && geo instanceof GeometryText) {
			engine.BASE.KEY._focus = ((GeometryText) geo);
			engine.BASE.KEY._focus.focusField.setValue(true,engine.BASE.KEY);
		}
	}
}
public static void sub(IntField valueA,IntField valueB, IntField target) throws LockException{

	target.setValue(valueA.getValue()-valueB.getValue() , target);
}
public static void sub(Point2DField vectorA,Point2DField vectorB, Point2DField target) throws LockException{

	target.setValueX(vectorA.getValueX()-vectorB.getValueX() , target);
	target.setValueY(vectorA.getValueY()-vectorB.getValueY() , target);
}
public static void translate(DataPoint source,TagModifier modifier, DataTranslation action, DataPoint target) throws LockException
{
	switch(modifier.flavour)
	{
//-------------------------------------------------------------------
		case TagModifier.POINT_TYPE_POINT_CODE:
			if(!source.hasChanged() &&
				!action.rotationView.hasChanged() &&
				!action.scaleView.hasChanged() &&
				!action.positionView.hasChanged()
			)
				return;

			//copy(source,target);

			rotate(source,action.rotationView,target);
			scale(target,action.scaleView,target);
			add(target,action.positionView,target);

			translationCount++;

			break;
//-------------------------------------------------------------------
		case TagModifier.POINT_TYPE_VECTOR_CODE:
			if(!source.hasChanged() &&
				!action.rotationView.hasChanged() &&
				!action.scaleView.hasChanged()
			)
				return;

			//target.setValueX(source.getValueX(),action);
			//target.setValueY(source.getValueY(),action);
			rotate(source,action.rotationView,target);
			scale(target,action.scaleView,target);

			translationCount++;

			break;
		case TagModifier.POINT_TYPE_BOUNDS_CODE:
/*				if(!source.hasChanged() &&
					!action.scaleView.hasChanged()
				}
					return;*/ // add in and test
				scale(source,action.scaleView,target);
				translationCount++;
			break;
	}
}
public static void translate(DataPoints source,TagModifier modifier, DataTranslation action, DataPoints target) throws LockException
{

	if(!source.hasChanged() &&
		!action.rotationView.hasChanged() &&
		!action.scaleView.hasChanged() &&
		!action.positionView.hasChanged()
	)
		return;

	while(target.size()>source.size())
	{
		target.remove(target.get(target.size()-1),action);
	}

	for(int loop=0;loop<source.size();loop++)
	{
		while(target.size()<loop+1)
			target.add(new DataPoint(),action);

		translate(source.get(loop),modifier,action,target.get(loop));
	}

}
public static void translate(DataScale source, DataTranslation action, DataScale target) throws LockException
{
	if(!source.hasChanged() &&
		!action.scaleView.hasChanged()
	)
		return;

	scale(source,action.scaleView,target);
	translationCount++;
}
public static void translate(DataSize source,TagModifier modifier, DataTranslation action, DataSize target) throws LockException
{
	switch(modifier.flavour)
	{
		case TagModifier.SIZE_TYPE_SIZE_CODE:
		case TagModifier.SIZE_TYPE_THICKNESS_CODE:
			if(!source.hasChanged() &&
				!action.scaleView.hasChanged()
			)
				return;
			mult(source,action.scaleView,target);
			translationCount++;
			break;
		case TagModifier.SIZE_TYPE_ANGLE_CODE:
			if(!source.hasChanged() &&
				!action.rotationView.hasChanged()
			)
				return;

			add(source,action.rotationView,target);
			translationCount++;
			break;
	}
}
public static void translate(DataState source, DataTranslation action, DataState target) throws LockException
{
	if(!source.hasChanged() &&
		!action.visibleView.hasChanged()
	)
		return;

	if(action.visibleView.getValue()==false)
	{
		target.setValue(false,action);
	}
	else
	{
		target.setValue(source.getValue(),source);
	}

	translationCount++;
}
public static void translate(Point2DField source, DataTranslation action, Point2DField target) throws LockException
{
	if(!source.hasChanged() &&
		!action.rotationView.hasChanged() &&
		!action.scaleView.hasChanged() &&
		!action.positionView.hasChanged()
	)
		return;

	copy(source,target);

	rotate(source,action.rotationView,target);
	scale(target,action.scaleView,target);
	add(target,action.positionView,target);

	translationCount++;
}


// TODO combine buildInstance methods
public static Object buildInstance(XmlElement element,GrfxMLEngine engine) throws Exception
{
	Class clazz  = Class.forName(NameHelper.toClassName(element.getName()) );

	Constructor constructor = null;

	try{
		constructor = clazz.getConstructor( new Class[]{Class.forName("org.one.stone.soup.grfxML.GrfxMLEngine")} );
	}
	catch(NoSuchMethodException me)
	{}

	Object newItem = null;
	if(constructor==null)
	{
		newItem = clazz.newInstance();
	}
	else
	{
		newItem = constructor.newInstance( new Object[]{engine} );
	}


	clazz.
		getMethod(NameHelper.BUILD_METHOD,NameHelper.BUILD_SIG).
			invoke(newItem,new Object[]{element});

	return newItem;
}

public static Object buildInstance(XmlElement element,GrfxMLEngine engine,DataNode parent) throws Exception
{
	Class clazz  = Class.forName(NameHelper.toClassName(element.getName()) );

	Constructor constructor = null;
	Object newItem = null;

	try{
		constructor = clazz.getConstructor( new Class[]{Class.forName("org.one.stone.soup.grfxML.GrfxMLEngine"),Class.forName("org.one.stone.soup.grfxML.DataNode")} );
		newItem = constructor.newInstance( new Object[]{engine,parent} );
	}
	catch(Exception me)
	{
		try{
			constructor = clazz.getConstructor( new Class[]{Class.forName("org.one.stone.soup.grfxML.DataNode")} );
			newItem = constructor.newInstance( new Object[]{parent} );
		}
		catch(Exception me2)
		{}
	}

	if(constructor==null)
	{
		newItem = clazz.newInstance();
	}

	clazz.
		getMethod(NameHelper.BUILD_METHOD,NameHelper.BUILD_SIG).
			invoke(newItem,new Object[]{element});

	return newItem;
}

public static Object buildInstance(ClassLoader classLoader,String className,GrfxMLEngine engine) throws Exception
{
	Class clazz  = classLoader.loadClass(className);

	Constructor constructor = clazz.getConstructor( new Class[]{Class.forName("org.one.stone.soup.grfxML.GrfxMLEngine")} );

	Object newItem = null;
	if(constructor==null)
	{
		newItem = clazz.newInstance();
	}
	else
	{
		newItem = constructor.newInstance( new Object[]{engine} );
	}

	return newItem;
}
}
